﻿using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

#if AGENTEDITOR
using FRL.DebtChased.AgentEditor;
#endif // #if AGENTEDITOR

#if CLIENTEDITOR
using FRL.DebtChased.ClientEditor;
#endif // #if CLIENTEDITOR

#if CHASEWORKSPACE
using FRL.DebtChased.ChaseWorkspace;
#endif // #if CHASEWORKSPACE

using FRL.DebtChased.Extensions;
using FRL.DebtChased.Repository;
using FRL.DebtChased.RichContentService;
using FRL.DebtChased.Silverlight.Extensions;

namespace FRL.DebtChased.Silverlight.Controls
{

	/// <summary>
	/// Declaration of ContactCtrl class
	/// </summary>
	public partial class ContactCtrl : UserControl
	{

		/////////////////////////////////////////////////////////////
		// Constants

		private const int FirstPointRowIndex = 2;

		private const string NewAddressHeading = "*New*";

		private Definitions.eContactPointType[] ContactPointSortOrder = new Definitions.eContactPointType[5]
		{ 
			Definitions.eContactPointType.DDI,
			Definitions.eContactPointType.Phone,
			Definitions.eContactPointType.Mobile,
			Definitions.eContactPointType.Fax,
			Definitions.eContactPointType.Email
		};


		/////////////////////////////////////////////////////////////
		// Delegates and events

		public event EventHandler<ChildCtrlContentChangeEventArg> ContentChanged;

		//public event EventHandler DeleteButtonClicked;

		/////////////////////////////////////////////////////////////
		// Attributes

		private Dictionary<string, string> m_dictCountries = null;
		public Dictionary<string, string> CountryDictionary
		{
			get { return m_dictCountries; }
			set { m_dictCountries = value; }
		}

		private Definitions.eContactType m_contactType;
		public Definitions.eContactType ContactType
		{
			get { return m_contactType; }
		}

		private int m_iContactId = 0;
		public int ContactId
		{
			get { return m_iContactId; }
			set { m_iContactId = value; }
		}

		public bool ShowUserAccount
		{
			get {
#if DEBUG
				Debug.Assert(m_tabContacts != null);
#endif // #if DEBUG
				return (
					(m_contactType == Definitions.eContactType.Client)
					&&
					(m_tabContacts.ShowUserAccount)
				);
			}
		}

		public bool ShowDOB
		{
			get
			{
#if DEBUG
				Debug.Assert(m_tabContacts != null);
#endif // #if DEBUG
				return (
					(m_contactType == Definitions.eContactType.Account)
					&&
					(m_tabContacts.ShowDOB)
				);
			}
		}

		private bool m_bBlockUpdates = true;

		private ContactTabCtrl m_tabContacts;

		/////////////////////////////////////////////////////////////
		// Construction

		public ContactCtrl( ContactTabCtrl tabContacts, Definitions.eContactType ContactType, int iContactId )
		{

			// Initialize member variables
			m_contactType = ContactType;
			m_iContactId = iContactId;
			m_tabContacts = tabContacts;

			// Load xaml
			InitializeComponent();

			// Set controls
			cboPointType.Items.Add(new ComboBoxObject(Definitions.eContactPointType.DDI.ToString(), (int)Definitions.eContactPointType.DDI, null));
			cboPointType.Items.Add(new ComboBoxObject(Definitions.eContactPointType.Phone.ToString(), (int)Definitions.eContactPointType.Phone, null));
			cboPointType.Items.Add(new ComboBoxObject(Definitions.eContactPointType.Mobile.ToString(), (int)Definitions.eContactPointType.Mobile, null));
			cboPointType.Items.Add(new ComboBoxObject(Definitions.eContactPointType.Fax.ToString(), (int)Definitions.eContactPointType.Fax, null));
			cboPointType.Items.Add(new ComboBoxObject(Definitions.eContactPointType.Email.ToString(), (int)Definitions.eContactPointType.Email, null));
			cboPointType.SelectedIndex = 0;

			// Set drop-downs
			drpTitle.StringList = CachedData.Singleton.ContactTitles;
			drpTitle.Category = Definitions.ReusableString_ContactTitles;
			drpPosition.StringList = CachedData.Singleton.ContactPositions;
			drpPosition.Category = Definitions.ReusableString_ContactPositions;

			// Set controls
			cboUserRole.Items.Add(new ComboBoxObject("User", (int)Definitions.eRoles.CLIENT_USER, null));
			cboUserRole.Items.Add(new ComboBoxObject("Admin", (int)Definitions.eRoles.CLIENT_ADMIN, null));
			cboUserRole.SelectedIndex = 0;

			// Add the new address
			_updateAddressTabHeading(_addAddressTab(true));

			// Update contact name text-block
			_updateContactName();

			// Unblock events
			m_bBlockUpdates = false;

		}

		private void UserControl_Loaded(object sender, RoutedEventArgs e)
		{

			// Setup controls
			_setupControls();

		}

		/////////////////////////////////////////////////////////////
		// Event handlers

		private void cboPointType_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{

			// Set the format of the point address box
			Definitions.eContactPointType pointType = (Definitions.eContactPointType)(cboPointType.SelectedValue as ComboBoxObject).ItemData;
			if(
				(pointType == Definitions.eContactPointType.DDI)
				||
				(pointType == Definitions.eContactPointType.Fax)
				||
				(pointType == Definitions.eContactPointType.Mobile)
				||
				(pointType == Definitions.eContactPointType.Phone)
			)
				txtPointAddress.Filter = ExTextBox.eFilter.PhoneNumber;
			else if(pointType == Definitions.eContactPointType.Email)
			{
				txtPointAddress.Filter = ExTextBox.eFilter.EmailAddress;
				txtPointAddress.LetterCase = ExTextBox.eLetterCase.Lower;
			}
#if DEBUG
			else
				Debug.Assert(false);
#endif // #if DEBUG

		}

		protected void butDropDownContact_Click(object sender, EventArgs e)
		{

			// Get height
			double dblHeight = gridContactName.Height;
			if(double.IsNaN(dblHeight))
				dblHeight = gridContactName.ActualHeight;

			// If grid needs expanding...
			if (dblHeight < gridContactName.MaxHeight)
				ExpandNamePanel(true);
			else
				ExpandNamePanel(false);

		}

		public void ExpandNamePanel(bool bExpand)
		{

			// If expanding...
			if (bExpand)
			{
				animDropPanel.Begin();
				butDropDownContact.IsUpsideDown = false;
			}
			else
			{
				animRaisePanel.Begin();
				butDropDownContact.IsUpsideDown = true;
			}

		}

		private void ContactAddressCtrl_ContentChanged(object sender, ChildCtrlContentChangeEventArg e)
		{

			// Find the tab item
			TabItem tabItem = (sender as FrameworkElement).FindParent(typeof(TabItem)) as TabItem;

			// Is this a new tab
			bool bNewTab = (tabAddress.Items.IndexOf(tabItem) == (tabAddress.Items.Count - 1));

			// If this is the new tab...
			if (bNewTab)
			{

				// Add another new tab
				TabItem newTabItem = _addAddressTab(true);
				_updateAddressTabHeading(newTabItem);

				// Fire the change event back
				_fireContentChangedEvent(false);

			}

			// Update tab heading
			if( (bNewTab) || (e.UpdateTabHeading) )
				_updateAddressTabHeading(tabItem);

			// Update header buttons
			if (bNewTab)
			{
				TabHeader tabHeader = tabItem.Header as TabHeader;
				(tabItem.Content as ContactAddressCtrl).ShowDeleteButton = true;
			}

		}

		private void butAddPoint_Click(object sender, EventArgs e)
		{

			// Check the details
			string strAddress = txtPointAddress.Text.Trim();
			if (strAddress.Length < 6)
			{

				// Message for user and exit
				App.PopupMessage("The new telephone number or email address entry must be at least 6 characters.");
				return;

			}

			// Create the contact point
			ContactPointTransport cpt = new ContactPointTransport()
			{
				Address = strAddress,
				ContactPointType = (cboPointType.SelectedValue as ComboBoxObject).ItemData
			};

			// Add new rows
			int iRowIndex = _insertPointRow(cpt);

			// Fire event
			_fireContentChangedEvent(false);

		}

		void pointCtrl_DeleteEvent(object sender, RoutedEventArgs e)
		{

			// Get the selected point-ctrl
			ContactPointCtrl pointCtrl = sender as ContactPointCtrl;
			int iRowIndex = Grid.GetRow(pointCtrl);

			// Delete the row
			_deletePointRow(iRowIndex, 1);

			// Fire event
			_fireContentChangedEvent(false);

		}

		private void TabHeader_AddClick(object sender, RoutedEventArgs e)
		{

			// Create a new add tab
			TabItem tabItem = _addAddressTab(true);
			_updateAddressTabHeading(tabItem);

			// Update the heading for the saved tab
			TabHeader tabHeader = sender as TabHeader;
			tabItem = tabHeader.FindParentByVisualTree(typeof(TabItem)) as TabItem;
			(tabItem.Content as ContactAddressCtrl).ShowDeleteButton = true;
			_updateAddressTabHeading(tabItem);

			// Fire event
			_fireContentChangedEvent(false);

		}

		void ctrlAddress_DeleteButtonClicked(object sender, EventArgs e)
		{

			// Update the heading for the saved tab
			ContactAddressCtrl addressCtrl = sender as ContactAddressCtrl;
			TabItem tabItem = addressCtrl.FindParent(typeof(TabItem)) as TabItem;
			int iIndex = tabAddress.Items.IndexOf(tabItem);

			// Remove this tab
			_removeAddressTab(iIndex);

			// Fire event
			_fireContentChangedEvent(false);

		}

		private void TextBox_ContentChanged(object sender, TextChangedEventArgs e)
		{

			// Fire event
			_fireContentChangedEvent(
				(sender == txtFirstName)
				||
				(sender == txtSurname)
				||
				(sender == txtAbbr)
			);

			// Update the contacts name
			_updateContactName();

		}

		private void DropDown_TextChanged(object sender, RoutedEventArgs e)
		{

			// Fire event
			_fireContentChangedEvent(false);

			// Update the contacts name
			_updateContactName();

		}

		private void butRandomPassword_Click(object sender, RoutedEventArgs e)
		{

			// Set controls
			txtPassword.Text = FRL.DebtChased.Extensions.Extensions.MakePassword(10);

		}

		private void tabAddress_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{

			// Get the selected tab
			ContactAddressCtrl addressCtrl = tabAddress.SelectedContent as ContactAddressCtrl;
			addressCtrl.PreDisplay();			

		}

		/////////////////////////////////////////////////////////////
		// Operations

		public void PreDisplay()
		{

			// Notify the currently selected address tab
			ContactAddressCtrl addressCtrl = tabAddress.SelectedContent as ContactAddressCtrl;
			addressCtrl.PreDisplay();			

		}

		private void _fireContentChangedEvent(bool bUpdateTabHeading)
		{

			// Shortcuts
			if (m_bBlockUpdates)
				return;
			if (ContentChanged == null)
				return;

			// Inform parent of change in textual content
			ContentChanged(
				this,
				new ChildCtrlContentChangeEventArg(bUpdateTabHeading)
			);

		}
		private void _setupControls()
		{
#if DEBUG
			Debug.Assert(m_contactType != Definitions.eContactType.NotSet);
#endif // #if DEBUG

			// Show/hide controls
			stackUsername.Visibility = this.ShowUserAccount ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
			txtUsername.Visibility = stackUsername.Visibility;
			stackPassword.Visibility = stackUsername.Visibility;
			gridPassword.Visibility = stackUsername.Visibility;
			stackRole.Visibility = stackUsername.Visibility;
			cboUserRole.Visibility = stackUsername.Visibility;

		}

		public void Set(ContactTransport contactTransport, bool bReloading)
		{

			// Declaration of local variables
			int i1 = 0;

			// Retain the id
			m_iContactId = contactTransport.ContactId;

			// First, make sure the new tabs are available
			if (tabAddress.Items.Count == 0)
			{
				TabItem tabItem = _addAddressTab(true);
				_updateAddressTabHeading(tabItem);
			}

			// Block updates
			m_bBlockUpdates = true;
			{

				// Set controls
				drpTitle.Text = contactTransport.Title ?? string.Empty;
				txtFirstName.Text = contactTransport.FirstName ?? string.Empty;
				txtInitials.Text = contactTransport.Initials ?? string.Empty;
				txtSurname.Text = contactTransport.Surname ?? string.Empty;
				txtAbbr.Text = contactTransport.Abbr ?? string.Empty;
				drpPosition.Text = contactTransport.Position ?? string.Empty;
				txtNotes.Text = contactTransport.Notes ?? string.Empty;
				if (contactTransport.DOB != null)
					dtDOB.SelectedDate = contactTransport.DOB.Value;
				if (this.ShowUserAccount)
				{
					txtUsername.Text = contactTransport.Username ?? string.Empty;
					txtPassword.Text = contactTransport.Password ?? string.Empty;
					ComboBoxObject.SelectByItemData(cboUserRole, contactTransport.UserRole);
				}

			}
			m_bBlockUpdates = false;

			// Update contact name
			_updateContactName();

			// How many point rows need creating
			int iExistingPointRows = Grid.GetRow(tbNewPoint) - FirstPointRowIndex;

			// If need to add new rows...
			if (iExistingPointRows < contactTransport.PointList.Count)
			{

				// Add new rows
				int iNewRowsRequired = contactTransport.PointList.Count - iExistingPointRows;
				_addPointRows(iNewRowsRequired);

			}
			else if (iExistingPointRows > contactTransport.PointList.Count)
			{

				// Delete rows
				int iRowsToDelete = iExistingPointRows - contactTransport.PointList.Count;
				_deletePointRow(FirstPointRowIndex + iExistingPointRows - iRowsToDelete, iRowsToDelete);

			}

			// Sort the contact points
			for (i1 = 0; i1 < contactTransport.PointList.Count; i1++)
			{
				bool bDelta = false;
				for (int i2 = 0; i2 < contactTransport.PointList.Count - 1; i2++)
				{
					int iSortOrderA = _getContactPointSortOrder( (Definitions.eContactPointType) contactTransport.PointList[i2].ContactPointType);
					int iSortOrderB = _getContactPointSortOrder( (Definitions.eContactPointType) contactTransport.PointList[i2 + 1].ContactPointType);
					if (iSortOrderA > iSortOrderB)
					{

						// Swap over
						ContactPointTransport temp = contactTransport.PointList[i2];
						contactTransport.PointList.RemoveAt(i2);
						contactTransport.PointList.Insert(i2 + 1, temp);

						// Update flag
						bDelta = true;

					}
				}
				if(!bDelta)
					break;
			}

			// Loop through the contact points
			for (i1 = 0; i1 < contactTransport.PointList.Count; i1++)
			{

				// Find the label and point-ctrl
				TextBlock tbLabel = null;
				ContactPointCtrl pointCtrl = null;
				_getPointControls(FirstPointRowIndex + i1, out tbLabel, out pointCtrl);

				// Set the label and point
				ContactPointTransport cpt = contactTransport.PointList[i1];
				tbLabel.Text = string.Format("{0}:", (Definitions.eContactPointType)cpt.ContactPointType);
				pointCtrl.Set(cpt);

			}

			// If need to create addresses...
			if (tabAddress.Items.Count < (contactTransport.AddressList.Count + 1))
			{

				// Add addresses
				int iNewAddressesAdded = contactTransport.AddressList.Count - tabAddress.Items.Count + 1;
				while (iNewAddressesAdded > 0)
				{
					_addAddressTab(false);
					iNewAddressesAdded--;
				} // while-loop

			}
			else if ((contactTransport.AddressList.Count + 1) < tabAddress.Items.Count)
			{

				// Remove addresses
				int iRemoveAddressCount = tabAddress.Items.Count - contactTransport.AddressList.Count - 1;
				while (iRemoveAddressCount > 0)
				{
					_removeAddressTab(tabAddress.Items.Count-2);
					iRemoveAddressCount--;
				} // while-loop

			}

			// Loop through the addresses
#if DEBUG
			Debug.Assert(tabAddress.Items.Count == (contactTransport.AddressList.Count + 1));
#endif // #if DEBUG
			for (i1 = 0; i1 < contactTransport.AddressList.Count; i1++)
			{

				// Set the address details
				TabItem tabItem = tabAddress.Items[i1] as TabItem;
				ContactAddressCtrl addrCtrl = tabItem.Content as ContactAddressCtrl;
				addrCtrl.Set(contactTransport.AddressList[i1]);

				// Contact if not reloading...
				if (!bReloading)
					addrCtrl.ExpandPanel(false);

				// Update the tab item heading
				_updateAddressTabHeading(tabAddress.Items[i1] as TabItem);

			}

			// Select the first tab
			if (!bReloading)
				tabAddress.SelectedIndex = 0;

		}

		public ContactTransport Get(List<string> Errors)
		{

			// Declare return variable
			ContactTransport Ftmp = new ContactTransport();
			Ftmp.PointList = new System.Collections.ObjectModel.ObservableCollection<ContactPointTransport>();
			Ftmp.AddressList = new System.Collections.ObjectModel.ObservableCollection<ContactAddressTransport>();

			// Check contact details
			if (
				(txtFirstName.Text.Length == 0)
				&&
				(txtSurname.Text.Length == 0)
				&&
				(txtAbbr.Text.Length == 0)
			)
				Errors.Add("A contact must have either a first name, a surname or an Aka.");
			if (tabAddress.Items.Count == 1)
				Errors.Add("A contact must have at least one address.");
			if (this.ShowUserAccount)
			{
				if (txtUsername.Text.Length < 6)
					Errors.Add("A contact username must be at least 6 characters.");
				if (txtPassword.Text.Length < 6)
					Errors.Add("A contact password must be at least 6 characters.");
			}

			// Update object
			Ftmp.Title = drpTitle.Text;
			Ftmp.FirstName = txtFirstName.Text;
			Ftmp.Initials = txtInitials.Text;
			Ftmp.Surname = txtSurname.Text;
			Ftmp.Abbr = txtAbbr.Text;
			Ftmp.Position = drpPosition.Text;
			Ftmp.Notes = txtNotes.Text;
			Ftmp.ContactId = m_iContactId;
			if (dtDOB.SelectedDate != null)
				Ftmp.DOB = dtDOB.SelectedDate.Value;
			if (this.ShowUserAccount)
			{
				Ftmp.Username = txtUsername.Text;
				Ftmp.Password = txtPassword.Text;
				Ftmp.UserRole = (cboUserRole.SelectedValue as ComboBoxObject).ItemData;
			}

			// Get the point controls
			List<ContactPointCtrl> pointCtrls = _getPointControls();
			foreach (ContactPointCtrl pointCtrl in pointCtrls)
				Ftmp.PointList.Add(pointCtrl.Get());

			// Get the address list
			for(int i1=0; i1<(tabAddress.Items.Count-1); i1++)
			{
				TabItem tabItem = tabAddress.Items[i1] as TabItem;
				ContactAddressCtrl contactAddressCtrl = tabItem.Content as ContactAddressCtrl;
				Ftmp.AddressList.Add(contactAddressCtrl.Get(Errors));
			}

			// Return variable
			return Ftmp;

		}

		private List<ContactPointCtrl> _getPointControls()
		{

			// Declare return variable
			List<ContactPointCtrl> Ftmp = new List<ContactPointCtrl>();

			// Loop through controls
			foreach (UIElement el in gridMain.Children)
			{
				
				// Check type
				if (el is ContactPointCtrl)
					Ftmp.Add(el as ContactPointCtrl);

			}

			// Return variable
			return Ftmp;

		}

		public void _addPointRows(int iNewRows)
		{

			// shortcuts
			if (iNewRows <= 0)
				return;

			// Declaration of local variables
			int i1 = 0;

			// Calculate insertion index
			int iExistingPointRows = Grid.GetRow(tbNewPoint) - FirstPointRowIndex;
			int iInsertIndex = FirstPointRowIndex + iExistingPointRows;

			// Create new rows
			for (i1 = 0; i1 < iNewRows; i1++)
			{
				RowDefinition newRow = new RowDefinition();
				newRow.Height = new GridLength(0, GridUnitType.Auto);
				gridMain.RowDefinitions.Insert(iInsertIndex, newRow);
			}

			// Move existing control down
			foreach (FrameworkElement el in gridMain.Children)
			{
				int iRow = Grid.GetRow(el);
				if (iRow >= iInsertIndex)
				{
					iRow += iNewRows;
					Grid.SetRow(el, iRow);
				}
			}

			// Add new row controls
			for (i1 = 0; i1 < iNewRows; i1++)
			{

				// Make a label
				TextBlock tbLabel = new TextBlock();
				tbLabel.Style = (App.Current as App).ResourceDictionary["DataGridLabelStyle"] as Style;

				// Make an editing control
				ContactPointCtrl pointCtrl = new ContactPointCtrl();
				pointCtrl.DeleteEvent += new RoutedEventHandler(pointCtrl_DeleteEvent);

				// Add to grid
				gridMain.Children.Add(tbLabel);
				Grid.SetRow(tbLabel, iInsertIndex + i1);
				gridMain.Children.Add(pointCtrl);
				Grid.SetColumn(pointCtrl, 1);
				Grid.SetColumnSpan( pointCtrl, 2);
				Grid.SetRow(pointCtrl, iInsertIndex + i1);

			}

		}

		private int _insertPointRow( ContactPointTransport cpt )
		{

			// Declaration of local variables
			int i1 = 0;
			TextBlock tbLabel = null;
			ContactPointCtrl pointCtrl = null;

			// Calculate existing point rows
			int iExistingPointRows = Grid.GetRow(tbNewPoint) - FirstPointRowIndex;

			// Find insertion row
			int iRowIndex = FirstPointRowIndex + iExistingPointRows;
			if (iExistingPointRows > 0)
			{

				// Find the sort order for the new contact point
				int iNewPointSortOrder = _getContactPointSortOrder( (Definitions.eContactPointType) cpt.ContactPointType );

				// Loop through points
				for (int iOffset = 0; iOffset < iExistingPointRows; iOffset++)
				{

					// Get the contact point
					tbLabel = null;
					pointCtrl = null;
					_getPointControls(FirstPointRowIndex + iOffset, out tbLabel, out pointCtrl);

					// Find the sort order for this row
					int iExistingPointSortOrder = _getContactPointSortOrder(pointCtrl.PointType);

					// If the current is beyond in sort order...
					if(iExistingPointSortOrder > iNewPointSortOrder)
					{
						iRowIndex = FirstPointRowIndex + iOffset;
						break;
					}

				}

			}

			// Insert row
			RowDefinition newRow = new RowDefinition();
			newRow.Height = new GridLength(0, GridUnitType.Auto);
			gridMain.RowDefinitions.Insert(iRowIndex, newRow);

			// Shift rows down
			gridMain.ShiftRowsDown(iRowIndex, 1);

			// Make a label
			tbLabel = new TextBlock();
			tbLabel.Style = (App.Current as App).ResourceDictionary["DataGridLabelStyle"] as Style;
			tbLabel.Text = string.Format("{0}:", (Definitions.eContactPointType)cpt.ContactPointType);

			// Make an editing control
			pointCtrl = new ContactPointCtrl();
			pointCtrl.DeleteEvent += new RoutedEventHandler(pointCtrl_DeleteEvent);
			pointCtrl.Set(cpt);

			// Add to grid
			gridMain.Children.Add(tbLabel);
			Grid.SetRow(tbLabel, iRowIndex);
			gridMain.Children.Add(pointCtrl);
			Grid.SetColumn(pointCtrl, 1);
			Grid.SetColumnSpan(pointCtrl, 2);
			Grid.SetRow(pointCtrl, iRowIndex);

			// Return row index
			return iRowIndex;

		}

		private void _deletePointRow(int iRowIndex, int iCount)
		{

			// Declaration of local variables
			int i1 = 0;

			// Remove control in these rows
			i1 = 0;
			while(i1 < gridMain.Children.Count)
			{

				// Get the next element
				FrameworkElement el = gridMain.Children[i1] as FrameworkElement;
				int iRow = Grid.GetRow(el);
				if (
					(iRow >= iRowIndex)
					&&
					(iRow < (iRowIndex + iCount))
				)
					gridMain.Children.RemoveAt(i1);
				else
					i1++;

			}

			// Shift lower controls upwards
			for ( i1 = 0; i1 < gridMain.Children.Count; i1++ )
			{

				// Get the next element
				FrameworkElement el = gridMain.Children[i1] as FrameworkElement;
				int iRow = Grid.GetRow(el);
				if (iRow > iRowIndex)
				{
					iRow -= iCount;
					Grid.SetRow(el, iRow);
				}

			}

			// Remove row definitions
			for (i1 = 0; i1 < iCount; i1++)
				gridMain.RowDefinitions.RemoveAt(iRowIndex);

		}

		public void _getPointControls( int iRowIndex, out TextBlock tbLabel, out ContactPointCtrl pointCtrl )
		{

			// Initialize member variables
			tbLabel = null;
			pointCtrl = null;

			// Find the label and point-ctrl
			foreach (FrameworkElement el in gridMain.Children)
			{
				int iRow = Grid.GetRow(el);
				if (iRow == iRowIndex)
				{
					if (Grid.GetColumn(el) == 0)
					{
#if DEBUG
						Debug.Assert(tbLabel == null);
#endif // #if DEBUG
						tbLabel = el as TextBlock;
#if DEBUG
						Debug.Assert(tbLabel != null);
#endif // #if DEBUG
					}
					else if (Grid.GetColumn(el) == 1)
					{
#if DEBUG
						Debug.Assert(pointCtrl == null);
#endif // #if DEBUG
						pointCtrl = el as ContactPointCtrl;
#if DEBUG
						Debug.Assert(pointCtrl != null);
#endif // #if DEBUG
					}
				}
				if( (tbLabel!=null) && (pointCtrl!=null) )
					break;
			}
#if DEBUG
			Debug.Assert(tbLabel != null);
			Debug.Assert(pointCtrl != null);
#endif // #if DEBUG

		}

		private TabItem _addAddressTab(bool bNewAddress)
		{

			// Create a new tab item
			TabItem tabItem = new TabItem();

			// Add an address control
			ContactAddressCtrl ctrlAddress = new ContactAddressCtrl() { Tag=0 };
			ctrlAddress.ContentChanged += new EventHandler<ChildCtrlContentChangeEventArg>(ContactAddressCtrl_ContentChanged);
			ctrlAddress.DeleteButtonClicked += new EventHandler(ctrlAddress_DeleteButtonClicked);
			ctrlAddress.ShowDeleteButton = (!bNewAddress);
			tabItem.Content = ctrlAddress;

			// Add a header control
			TabHeader tabHeader = new TabHeader();
			tabItem.Header = tabHeader;

			// Insert into the tab control
			if (bNewAddress)
			{
				tabAddress.Items.Add(tabItem);
				ctrlAddress.ExpandPanel(true);
			}
			else
			{
				tabAddress.Items.Insert(tabAddress.Items.Count - 1, tabItem);
			}

			// Return new tab item
			return tabItem;

		}

		private void _removeAddressTab(int iOffset)
		{

			// Remove tab-item from control
			tabAddress.Items.RemoveAt(iOffset);

		}

		private void _updateAddressTabHeading(TabItem tabItem)
		{

			// Get the address control
			ContactAddressCtrl ctrlAddress = tabItem.Content as ContactAddressCtrl;
#if DEBUG
			Debug.Assert(ctrlAddress != null);
#endif // #if DEBUG

			// Compose label
			string strLabel;
			int iIndex = tabAddress.Items.IndexOf(tabItem);
			if (iIndex == (tabAddress.Items.Count - 1))
				strLabel = NewAddressHeading;
			else
				strLabel = ctrlAddress.ComposeTabHeading();

			// Set label
			(tabItem.Header as TabHeader).Label = strLabel.DotDotDot(14);

		}

		public string ComposeTabHeading()
		{

			// Declare return variable
			string strFtmp;

			// If there is a nickname...
			if (txtAbbr.Text.Length > 0)
				strFtmp = txtAbbr.Text;
			else if (
				(txtFirstName.Text.Length > 0)
				||
				(txtSurname.Text.Length > 0)
			)
				strFtmp = string.Format("{0} {1}", txtFirstName.Text, txtSurname.Text).Trim();
			else
				strFtmp = "[contact]";

			// Return variable
			return strFtmp;

		}

		private void _updateContactName()
		{

			// Build string
			StringBuilder sb = new StringBuilder();
			sb.Append(drpTitle.Text);
			sb.Append(" ");
			sb.Append(txtFirstName.Text);
			sb.Append(" ");
			sb.Append(txtInitials.Text);
			sb.Append(" ");
			sb.Append(txtSurname.Text);
			sb.Append(" ");
			if (txtAbbr.Text.Length > 0)
				sb.AppendFormat(" aka \"{0}\"",txtAbbr.Text);
			sb.Append(" ");
			if(drpPosition.Text.Length > 0)
				sb.AppendFormat(" - {0}",drpPosition.Text);
			sb = sb.Replace("  ", " ");

			// If there is no contact name...
			string strContactName = sb.ToString().Trim();
			if (strContactName.Length == 0)
				strContactName = "[no name]";

			// Update controls
			tbContactName.Text = strContactName;

		}

		private int _getContactPointSortOrder(Definitions.eContactPointType pointType)
		{

			// Declare return variable
			int iFtmp = -1;

			// Find the sort order for this row
			for (int i1 = 0; i1 < ContactPointSortOrder.Length; i1++)
			{
				if (pointType == ContactPointSortOrder[i1])
				{
					iFtmp = i1;
					break;
				}
			}
#if DEBUG
			Debug.Assert(iFtmp != -1);
#endif // #if DEBUG

			// Return variable
			return iFtmp;

		}

	}
	
}
